﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Deployment.BuildManager.ExternalProcess.Common;
using PSE.Deployment.SetupBase;
using PSE.Deployment.SetupBase.Configuration;
using PSE.Deployment.SetupBase.Configuration.Actions;
using PSE.Deployment.Util;
using Sinacor.Deployment.Setup.Configuration;

namespace Deployment.BuildManager.ExternalProcess.CreateXml
{
    public class BuildTask : IBuildTask
    {
        #region IBuildTask Members

        public SetupInfo Execute(SetupInfo info)
        {
            this.SendMessage("Criando Arquivo de Configuração do Setup...");
                        
            if (info.ProductName.Equals("Sinacor", StringComparison.OrdinalIgnoreCase))
            {
                SinacorConfiguration setupConfig = SinacorCreateSetupConfig(info);
                List<Group> groupsToInstall = GetGroupsToInstall(info, setupConfig);
                SinacorSerializabeSetupXml(info, setupConfig, groupsToInstall);
            }
            else
            {
                SetupConfiguration setupConfig = CreateSetupConfig(info);
                List<Group> groupsToInstall = GetGroupsToInstall(info, setupConfig);
                SerializabeSetupXml(info, setupConfig, groupsToInstall);
            }

            return info;
        }

        public void Cancel()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Private [ Methods ]

        private List<Group> GetGroupsToInstall(SetupInfo info,
                                               SetupConfiguration setupConfig)
        {
            List<ActionBase> actions = new List<ActionBase>();
            List<FolderInfo> folders = new List<FolderInfo>();
            List<Group> groupsToInstall = new List<Group>();

            List<Group> groups = setupConfig.SystemInfo.Groups.SelectDeep(g => g.SubGroups).SelectDeep(g => g.Dependencies).Distinct(new GroupComparer()).OrderBy(g => g.Index).ToList();

            //string setupCompiledPath = string.Empty;

            List<ResourceItem> distinctFiles = info.CompiledFiles.Distinct().ToList();

            if (distinctFiles.Count <= 0)
            {
                throw new Exception("Falha no empacotamento. Branch inválido ou vazio");
            }

            foreach (ResourceItem file in distinctFiles)
            {
                //if (info.SetupUri.StartsWith("$"))
                //{
                //    setupCompiledPath = string.Concat(info.SetupPath,
                //                                      Path.DirectorySeparatorChar,
                //                                      BuildConstants.SetupBinFolder,
                //                                      Path.DirectorySeparatorChar,
                //                                      file.CompiledFullPath);
                //}
                //else
                //{
                //    setupCompiledPath = string.Concat(info.SetupUri, "\\x86\\",
                //                                      BuildConstants.SetupBinFolder,
                //                                      Path.DirectorySeparatorChar,
                //                                      file.CompiledFullPath);
                //}

                foreach (Group grp in (from g in groups
                                       where g.Module.Id == info.ModuleName
                                       where g.Actions.Count > 0
                                       select g).ToList())
                {
                    //se ha actions, verificar quais referenciam algums dos arquivos
                    if (grp.Actions != null && grp.Actions.Count > 0)
                    {
                        
                        foreach (ActionBase action in grp.Actions.FindAll(a => a.HasFile(file.ExtractionRelativePath)))
                            actions.Add(action);

                        if (actions != null && actions.Count > 0)
                        {
                            //actions = actions.Union<ActionBase>(temp, new ActionComparer()).ToList<ActionBase>();
                            Group cloneGroup = new Group();
                            cloneGroup.Actions = new List<ActionBase>(actions);
                            cloneGroup.Description = grp.Description;
                            cloneGroup.Id = grp.Id;
                            cloneGroup.Index = grp.Index;
                            cloneGroup.Install = grp.Install;
                            cloneGroup.Installed = grp.Installed;
                            cloneGroup.ParentGroup = grp.ParentGroup;
                            cloneGroup.Layers = grp.Layers;
                            cloneGroup.Module.Id = grp.Module.Id;
                            cloneGroup.Module.Name = grp.Module.Name;
                            cloneGroup.Name = grp.Name;

                            cloneGroup.System = new SystemInfo();
                            cloneGroup.System.Id = grp.System.Id;
                            cloneGroup.System.Description = grp.System.Description;
                            cloneGroup.System.Version = info.Version;
                            groupsToInstall = groupsToInstall.Union<Group>(new List<Group>() { cloneGroup }, new GroupComparer()).ToList<Group>();

                            actions.Clear();
                        }
                    }
                }
            }
            return groupsToInstall;
        }

        public bool HasFileInSetup(string setupFilePath, string fileName)
        {
            if (Directory.Exists(setupFilePath))
            {
                if (System.IO.File.Exists(string.Concat(setupFilePath, fileName)))
                    return true;
                else
                {
                    string[] subDirs = Directory.GetDirectories(setupFilePath);
                    foreach (string dir in subDirs)
                        HasFileInSetup(string.Concat(setupFilePath, Path.DirectorySeparatorChar, dir), fileName);

                    return false;
                }
            }
            else
                return false;
        }

        private void SinacorSerializabeSetupXml(SetupInfo info,
                                         SinacorConfiguration setupConfig,
                                         List<Group> groupsToInstall)
        {
            setupConfig.SystemInfo.Groups.Clear();
            setupConfig.DataBase.Versions.Clear();
            setupConfig.SystemInfo.Groups.AddRange(groupsToInstall);
            setupConfig.SystemInfo.Version = info.Version;
            setupConfig.SystemInfo.UpdateInfo.Descriptions = info.BuildDescriptions;
            setupConfig.SystemInfo.UpdateInfo.Name = info.PackageTitle;

            string resoureXmlPath = string.Concat(info.SetupTemplateProjectPath,
                                                  Path.DirectorySeparatorChar,
                                                  BuildConstants.ResourceFolder,
                                                  Path.DirectorySeparatorChar,
                                                  BuildConstants.SetupXmlConfigFileName);

            PSE.Deployment.Util.Serializer.Serialize<SinacorConfiguration>(setupConfig, resoureXmlPath);
        }


        private void SerializabeSetupXml(SetupInfo info,
                                         SetupConfiguration setupConfig,
                                         List<Group> groupsToInstall)
        {
            setupConfig.SystemInfo.Groups.Clear();
            setupConfig.SystemInfo.Groups.AddRange(groupsToInstall);
            setupConfig.SystemInfo.Version = info.Version;
            setupConfig.SystemInfo.UpdateInfo.Descriptions = info.BuildDescriptions;
            setupConfig.SystemInfo.UpdateInfo.Name = info.PackageTitle;

            string resoureXmlPath = string.Concat(info.SetupTemplateProjectPath,
                                                  Path.DirectorySeparatorChar,
                                                  BuildConstants.ResourceFolder,
                                                  Path.DirectorySeparatorChar,
                                                  BuildConstants.SetupXmlConfigFileName);

            PSE.Deployment.Util.Serializer.Serialize<SetupConfiguration>(setupConfig, resoureXmlPath);
        }

        private SinacorConfiguration SinacorCreateSetupConfig(SetupInfo info)
        {
            string path = string.Empty;

            if (info.SetupUri.StartsWith("$"))
            {
                path = info.SetupPath + Path.DirectorySeparatorChar + BuildConstants.SetupXmlConfigFileName;
            }
            else
            {
                path = info.SetupUri + "\\x86\\" + BuildConstants.SetupXmlConfigFileName;
            }

            SinacorConfiguration setupConfig = PSE.Deployment.Util.Serializer.Deserialize<SinacorConfiguration>(path);

            return setupConfig;
        }

        private SetupConfiguration CreateSetupConfig(SetupInfo info)
        {
            string path = string.Empty;

            if (info.SetupUri.StartsWith("$"))
            {
                path = info.SetupPath + Path.DirectorySeparatorChar + BuildConstants.SetupXmlConfigFileName;
            }
            else
            {
                path = info.SetupUri + "\\x86\\" + BuildConstants.SetupXmlConfigFileName;
            }

            SetupConfiguration setupConfig = PSE.Deployment.Util.Serializer.Deserialize<SetupConfiguration>(path);

            return setupConfig;
        }

        #endregion

        #region [ Events ]

        public event EventHandler<MessageEventArgs> MessageReceived;

        private void SendMessage(string message)
        {
            if (MessageReceived != null)
            {
                MessageEventArgs eventArgs = new MessageEventArgs(message);
                Logger.Log(message, Logger.EntryType.Information);
                MessageReceived(this, eventArgs);
            }
        }

        #endregion [ Events ]
    }
}
